package com.example.net1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.SignedObject;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SealedObject;
import javax.crypto.SecretKey;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {

	public static final String SERVER_IP = "128.61.42.136";//"143.215.119.112";
	public static final int PORT = 55555;	
	public static final String clientReq = "Hello message";	
	public static final String bankInfo = "Bank Information";	
	public static final String pathToCert = "serverCA.crt";
		
	//EditText textOut;
	TextView receiptContent;
	Button buttonSend;
	Button buttonPay;
	Button buttonReceipt;
	TextView statusContent;
	Button saveReceiptButton;

	private PublicKey bankPublicKey;
	public static Receipt receipt;
	public static long nonce;
	
	Socket socket = null;
	InputStream is = null;
	OutputStream os = null;		
	ObjectOutputStream oos;  
	ObjectInputStream ois; 
	
	File dir;
	
	Message process(Message m) throws UnsupportedEncodingException{
		 receiptContent.setText(m.getTag());
		 return null;
	 }
	
	private void getBankPublicKey() {
//		KeyPairGenerator keyGen = null;
//		try {
//			keyGen = KeyPairGenerator.getInstance("RSA", "BC");
//		} catch (NoSuchAlgorithmException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (NoSuchProviderException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		keyGen.initialize(2048); // at least 2048bits so that the modulus is greater than the data_to_be_encrypted size
//        bankPublicKey = keyGen.genKeyPair().getPublic();
        try {
			bankPublicKey = CryptoUtil.getRSApublicKey(getAssets().open(pathToCert));
		} catch (IOException e) {
			e.printStackTrace();
		}  
	}
	
	@SuppressLint("NewApi") protected void onCreate(Bundle savedInstanceState) {
				
		super.onCreate(savedInstanceState);
    	StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    	StrictMode.setThreadPolicy(policy); 
		
		setContentView(R.layout.activity_main);

		//textOut = (EditText)findViewById(R.id.textout);
		buttonSend = (Button)findViewById(R.id.send);
		buttonPay = (Button)findViewById(R.id.pay);
		receiptContent = (TextView)findViewById(R.id.receiptContent);
		statusContent = (TextView)findViewById(R.id.textstatus);
		buttonSend.setOnClickListener(buttonSendOnClickListener);
		buttonPay.setVisibility(View.GONE);
		buttonPay.setOnClickListener(buttonPayOnClickListener);
		
		buttonReceipt = (Button)findViewById(R.id.receiptButton);
		buttonReceipt.setVisibility(View.GONE);
		
		saveReceiptButton = (Button)findViewById(R.id.saveReceiptButton);
		saveReceiptButton.setVisibility(View.GONE);
		
		buttonReceipt.setOnClickListener(new Button.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent = new Intent(MainActivity.this, ReceiptActivity.class);
		        startActivity(intent);
			}
		});
		
		saveReceiptButton.setOnClickListener(new Button.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				writeReceiptToSDFile();
			}
		});
		
		ContextWrapper context = new ContextWrapper(getApplicationContext());
		this.dir = context.getDir("mobilepayment", Context.MODE_PRIVATE);
		
		getBankPublicKey();
		encryptAndStoreBankInfo("Timothee", "Lenel", 
				1234567890123456L, 123, 0);
		
		}

	Button.OnClickListener buttonSendOnClickListener
	= new Button.OnClickListener(){
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub	
			statusContent.setText("Starting Transaction, Waiting for receipt ticket");
			connectToCashDesk();
			getReceipt();
			//		ClientMessage clientMessage =  (ClientMessage)ois.readObject(); 
		}
	};
	
	public void connectToCashDesk() {
		try {
			socket = new Socket(SERVER_IP, PORT); // assume the cash desk server is listening on this port
			is =  socket.getInputStream();
			os =  socket.getOutputStream();				
			oos = new ObjectOutputStream(os);  
			ois = new ObjectInputStream(is);  					
//			Message mes = new Message(clientReq);					
//			oos.writeObject(mes); 		
//			oos.flush();
			
//			Message serverMessage =  (Message)ois.readObject(); 
//			textIn.setText(serverMessage.getTag());
			buttonSend.setVisibility(View.GONE);
			buttonPay.setVisibility(View.VISIBLE);
			buttonReceipt.setVisibility(View.GONE);
			
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	
	public void getReceipt() {
		try {			
			receipt = (Receipt)ois.readObject();
			receiptContent.setText("Receipt " + receipt.amount + " USD");
			statusContent.setText("Receipt Received");
			buttonReceipt.setVisibility(View.VISIBLE);
			saveReceiptButton.setVisibility(View.VISIBLE);
		} catch (OptionalDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	Button.OnClickListener buttonPayOnClickListener
	= new Button.OnClickListener(){
		@Override
		public void onClick(View v) {
			statusContent.setText("Waiting for payment receipt");
			sendBankTicket(); 
			String transactionReceiptNb="";
			try {
				transactionReceiptNb = getTransactionReceipt();
			} catch (OptionalDataException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			statusContent.setText("Transaction Succeeded, Thank you");
			receiptContent.setText(transactionReceiptNb);
			
		}
	};
	
	public String getTransactionReceipt() throws OptionalDataException, ClassNotFoundException, IOException{
		String transactionReceiptNb = (String) ois.readObject();
		return transactionReceiptNb;
	}
	
	public void sendBankTicket() {
		try {
			oos.writeObject(new Integer(1));
			// If demand accepted, then encrypt the bank info
			// with the nonce and the POS bank id
			SignedObject signedPaymentRequest = (SignedObject) ois.readObject();
			
			// Verify if the payment request has really been signed
			// by the cash desk
			Signature signature = 
					Signature.getInstance("SHA1withRSA", "BC");
			boolean verification = 
					signedPaymentRequest.verify(
							CryptoUtil.getRSApublicKey(getAssets().open(pathToCert)), signature);
			
			System.out.println("[Client] Payment request signature verification: " + verification);
			if (!verification) {
				return;
			}
			
			PaymentRequest paymentRequest = (PaymentRequest) signedPaymentRequest.getObject();
			if (paymentRequest.amount != receipt.amount) { // check if cash desk did not change the amount to be payed
				return;
			}
			Long sellerId = paymentRequest.sellerId;
			nonce = paymentRequest.nonce;		
			//System.out.println("[Client] < sellerId=" + sellerId);
			
			receiptContent.setText("[Client] < sellerId=" + sellerId);
			System.out.println("[Client] < nonce=" + nonce);
			writeReceiptToSDFile();
			BankTicket bankTicket = new BankTicket(loadEncryptedBankInformation(), signedPaymentRequest);
			SecretKey key = generateKey();
			// Send AES encrypted bank ticket and RSA encrypted AES key
			EncryptedObject encryptedBankTicket = new EncryptedObject(
					BankInformation.encrypt(bankTicket, key), encryptKey(key, bankPublicKey));
			
			oos.writeObject(encryptedBankTicket);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}
	

	public void encryptAndStoreBankInfo(String firstName, String lastName,
			long cardNumber, int cvvNumber,
			long expirationDate) {
		SecretKey key = generateKey();
		try {
			BankInformation bankInfo = new BankInformation(firstName, lastName, 
					cardNumber, cvvNumber, expirationDate);
			File bankInformationFile = new File(dir, "bank_information");
			bankInformationFile.delete();
			FileOutputStream fos = new FileOutputStream(bankInformationFile);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			EncryptedObject encryptedBankInformation = new EncryptedObject(
					BankInformation.encrypt(bankInfo, key), encryptKey(key, bankPublicKey));
			oos.writeObject(encryptedBankInformation);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public EncryptedObject loadEncryptedBankInformation() {
		EncryptedObject encryptedBankInformation = null;
		FileInputStream fis;
		try {
			File bankInformationFile = new File(dir, "bank_information");
			fis = new FileInputStream(bankInformationFile);
			ObjectInputStream ois = new ObjectInputStream(fis);
			encryptedBankInformation = (EncryptedObject) ois.readObject();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return encryptedBankInformation;
	}

	public SealedObject encryptKey(SecretKey key, PublicKey bankPublicKey) {
		SealedObject encryptedKey = null;
		try {
			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.ENCRYPT_MODE, bankPublicKey);
			encryptedKey = new SealedObject(key, cipher);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return encryptedKey;
	}

	public SecretKey decryptKey(SealedObject encryptedKey, PrivateKey bankPrivateKey) {
		SecretKey decryptedKey = null;
		try {
			decryptedKey = (SecretKey) encryptedKey.getObject(bankPrivateKey);
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return decryptedKey;
	}
	
	public SecretKey generateKey() {
		SecretKey key = null;
		try {
			KeyGenerator generator = KeyGenerator.getInstance("AES");
			generator.init(new SecureRandom());
			key = generator.generateKey();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return key;
	}
	
	public static class ReceiptActivity extends ListActivity {
		public ReceiptActivity() {
			super();
		}
		public void onCreate(Bundle icicle) {
			super.onCreate(icicle);
			//String[] values = new String[];
			ArrayList<String> values = new ArrayList<String>();
			for (Item item : MainActivity.receipt.items) {
				String value = item.description + " (" + item.price + " )" + " x" + item.number + ": " + (item.price*item.number);
				values.add(value);
			}
			values.add("Total: " + receipt.amount);
			String[] array = values.toArray(new String[values.size()]);
			// Use your own layout
			ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
					R.layout.rowlayout, R.id.label, array);
			setListAdapter(adapter);
		}

		@Override
		protected void onListItemClick(ListView l, View v, int position, long id) {
			String item = (String) getListAdapter().getItem(position);
		}
	} 
	
	private void writeReceiptToSDFile() {
	    File root = android.os.Environment.getExternalStorageDirectory(); 
	    if (Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState())) {
	    	System.out.println("SDCard ready");
	    	File dir = new File (root.getAbsolutePath() + "/download");
	    	dir.mkdirs();
	    	File file = new File(dir, "receipt_" + nonce + ".htm");
	    	ReceiptHtmlAdapter receiptHtmlAdapter = new ReceiptHtmlAdapter(receipt, nonce);
	    	receiptHtmlAdapter.write(file);
	    }
	}
	
}